﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Lexical.Coding;
using System.Diagnostics;
using Kokomo.Lexical.Expressions;

namespace Kokomo.PeInspector.Reflection.Disassembly.Flow
{
	class LoopFlowAnalyzer : FlowAnalyzer
	{
		public LoopStatement LoopStatement { get; private set; }

		public LoopFlowAnalyzer(
			FlowAnalyzer parentAnalyzer,
			LoopStatement loopStatement,
			int blockStartOffset,
			int blockEndOffset,
			int bodyStartOffset,
			int bodyEndOffset
			)
			: base(parentAnalyzer, blockStartOffset, blockEndOffset, bodyStartOffset, bodyEndOffset)
		{
			this.LoopStatement = loopStatement;
			this.ExtraStatements = new List<Statement>();
		}

		public List<Statement> ExtraStatements { get; private set; }

		protected override void OnEnterBody()
		{
			base.OnEnterBody();
			this.ActiveStatementBlock = this.LoopStatement.Statements;
		}

		protected override void OnExitBlock()
		{
			base.OnExitBlock();

			//Debug.Assert(this.LoopStatement.Condition != null, "Null loop condition");
		}

		protected override void OnExitBody()
		{
			base.OnExitBody();
			//this.ActiveStatementBlock = this.ExtraStatements;
		}

		protected override void AnalyzeInnerBranch(MsilBranch branch, FlowAnalyzer innerFlow)
		{
			if (branch.TargetOffset == this.BodyEndOffset)
			{
				this.Context.RecordStatement(new LoopContinueStatement());
				this.Context.Advance();
			}
			else if (this.IsBranchToSubsequentBlock(branch))
			{
				this.Context.RecordStatement(new LoopExitStatement());
				this.Context.Advance();
			}
			else
			{
				base.AnalyzeInnerBranch(branch, innerFlow);
			}
		}

		private void OnLoopCondition(Expression condition)
		{
			condition = condition.CoerceTo(typeof(bool));
			if (this.LoopStatement.Condition == null)
				this.LoopStatement.Condition = condition;
			else
				this.LoopStatement.Condition = Expression.op_LogicalAnd(this.LoopStatement.Condition, condition);
		}

		protected override void AnalyzeLocalConditionalBranch(MsilBranch branch)
		{
			Instruction currentInstruction = this.Context.CurrentInstruction;
			if (
				(currentInstruction.Offset >= this.BodyEndOffset)
				&& (this.IsBranchToSubsequentBlock(branch))
				)
			{
				Expression exitCondition = this.Context.PopExpression();
				this.OnLoopCondition(!exitCondition);
				this.Context.Advance();
			}
			else if (currentInstruction.NextOffset == this.BlockEndOffset)
			{
				Expression condition = this.Context.PopExpression();
				BoundVariableReferenceExpression varReference = condition as BoundVariableReferenceExpression;
				if (
					(varReference != null)
					&& (this.LoopStatement.Statements.Count > 0)
					)
				{
					AssignmentStatement assignment = this.LoopStatement.Statements.Last() as AssignmentStatement;
					if (
						(assignment != null)
						&& (assignment.Variable.Equals(varReference)))
					{
						condition = assignment.Initializer;
						this.LoopStatement.Statements.Remove(assignment);
					}
				}

				this.OnLoopCondition(condition);
				this.Context.Advance();
			}
			else
			{
				base.AnalyzeLocalConditionalBranch(branch);
			}
		}
	}
}
